/* *****************************************************************
    MESQUITE -- The Mesh Quality Improvement Toolkit

    Copyright 2007 Sandia National Laboratories.  Developed at the
    University of Wisconsin--Madison under SNL contract number
    624796.  The U.S. Government and the University of Wisconsin
    retain certain rights to this software.

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    (lgpl.txt) along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    (2007) kraftche@cae.wisc.edu

  ***************************************************************** */

/** \file MsqIMesh.hpp
 *  \brief Adaptor for ITAPS iMesh interface
 *  \author Jason Kraftcheck
 */

#ifndef MSQ_MOAB_HPP
#define MSQ_MOAB_HPP

#include "MeshInterface.hpp"
#include "moab/Core.hpp"

namespace MBMesquite
{

/** The name of the tag (integer) that Mesquite will use
 *  to store internal data
 */
const char* const VERTEX_BYTE_TAG_NAME = "MesquiteVertexByte";

/**\class MsqIMesh
 *\brief Mesquite iMesh Adapter
 *
 * Adpater for interfacing Mesquite with an application that provides
 * the ITAPS iMesh interface for interacting with mesh data.
 */
class MsqMOAB : virtual public MBMesquite::Mesh
{
  public:
    //********* Functions that are NOT inherited ************

    MsqMOAB();
    virtual ~MsqMOAB();

    /**\brief Create iMesh adaptor instance
     *\param imesh      The iMesh instance to interact with
     *\param meshset    The set of elements for which to optimize the quality
     *
     *\param element_dimension Optimize the subset of the elements in
     *                  'meshset' with this dimension.  Pass iBase_ALL_TYPES
     *                  for both iBase_FACE and iBase_REGION elements.
     *                  If 'meshset' is the root set, then this argument is
     *                  typically either iBase_FACE or iBase_REGION.
     *
     *\param fixed_tag  A pointer to the tag handle designating a tag
     *                  containing fixed flags for vertices.  The tag must
     *                  have size==1 and type==iBase_INTEGER or iBase_BYTES.
     *                  A value of one for a vertex indicates that the vertex
     *                  position is fixed.  A value of zero indicates a free
     *                  vertex (a vertex that Mesquite may move to improve
     *                  mesh quality.)  If this tag is not specified, then
     *                  the boundary constraints of the optimization must be
     *                  specified using an MsqIRel instance.
     *
     *\param slaved_tag A pointer to the tag handle designating a tag
     *                  containing slaved flags for vertices.  The tag must
     *                  have size==1 and type==iBase_INTEGER or iBase_BYTES.
     *                  This tag value is ignored for corner vertices of
     *                  elements.  The concept of 'slaved' is applicable only
     *                  to non-corner vertices in higher-order elements. A
     *                  value of one for a vertex indicates that the vertex
     *                  position should is a fucntion of the element shape
     *                  function, while a zero value indicates that the
     *                  element shape function is a function of the vertex
     *                  position.
     */
    MsqMOAB( moab::Core* imesh, moab::EntityHandle meshset, moab::EntityType element_dimension, MsqError& err,
             const moab::Tag* fixed_tag = 0, const moab::Tag* slaved_tag = 0 );

    /**\brief Create iMesh adaptor instance
     *
     * This constructor is provided for the creation of an MsqIMesh instance
     * that is to be re-used for multiple optimization calls for different
     * sets of elements.  set_active_set must be called to define the
     * subset of the mesh to optimize before an instance constructed with
     * this constructor may be used in an optimization.
     *
     *\param imesh      The iMesh instance to interact with
     *
     *\param element_dimension Optimize the subset of the elements in
     *                  root set with this dimension.  Pass iBase_ALL_TYPES
     *                  for both iBase_FACE and iBase_REGION elements.
     *                  If 'meshset' is the root set, then this argument is
     *                  typically either iBase_FACE or iBase_REGION.
     *
     *\param fixed_tag  A pointer to the tag handle designating a tag
     *                  containing fixed flags for vertices.  The tag must
     *                  have size==1 and type==iBase_INTEGER or iBase_BYTES.
     *                  A value of one for a vertex indicates that the vertex
     *                  position is fixed.  A value of zero indicates a free
     *                  vertex (a vertex that Mesquite may move to improve
     *                  mesh quality.)  If this tag is not specified, then
     *                  the boundary constraints of the optimization must be
     *                  specified using an MsqIRel instance.
     *
     *\param slaved_tag A pointer to the tag handle designating a tag
     *                  containing slaved flags for vertices.  The tag must
     *                  have size==1 and type==iBase_INTEGER or iBase_BYTES.
     *                  This tag value is ignored for corner vertices of
     *                  elements.  The concept of 'slaved' is applicable only
     *                  to non-corner vertices in higher-order elements. A
     *                  value of one for a vertex indicates that the vertex
     *                  position should is a fucntion of the element shape
     *                  function, while a zero value indicates that the
     *                  element shape function is a function of the vertex
     *                  position.
     */
    MsqMOAB( moab::Core* imesh, moab::EntityType element_dimension, MsqError& err, const moab::Tag* fixed_tag = 0,
             const moab::Tag* slaved_tag = 0 );

    /** \brief set mesh to be smoothed.
     *
     * Set the mesh which Mesquite is to smooth.  Optionally
     * specify fixed vertices.
     * NOTE: If an active set is not specified, the default
     *       is to use the global set (the ENTIRE mesh.)
     *
     *\param element_set ITAPS entity set handle for set containing
     *                  mesh elements and vertices for which quality
     *                  is to be improved.
     */
    void set_active_set( moab::EntityHandle meshset, moab::EntityType element_dimension, MsqError& err );

    void set_fixed_tag( moab::Tag tag, MsqError& err );   //!< Set tag for vertex fixed flag
    void set_slaved_tag( moab::Tag tag, MsqError& err );  //!< Set tag for vertex slaved flag
    void clear_fixed_tag();                               //!< No tag for vertex fixed flag
    void clear_slaved_tag();                              //!< No tag for vertex fixed flag
    const moab::Tag* get_fixed_tag() const;               //!< Get tag for vertex fixed flag
    const moab::Tag* get_slaved_tag() const;              //!< Get tag for vertex slaved flag

    virtual moab::Core* get_interface() const;
    virtual moab::EntityHandle get_entity_set() const;

    //********* Functions that ARE inherited ************

    /**\brief Get dimension of vertex coordinates (2D vs. 3D). */
    virtual int get_geometric_dimension( MBMesquite::MsqError& /*err*/ );

    /** \brief Get handles for all elemnents */
    virtual void get_all_elements( std::vector< ElementHandle >& elements, MsqError& err );

    /** \brief Get handles for all vertices */
    virtual void get_all_vertices( std::vector< VertexHandle >& vertices, MsqError& err );

    /**\brief Query "fixed" flag for a vertex */
    virtual void vertices_get_fixed_flag( const VertexHandle vert_array[], std::vector< bool >& fixed_flag_array,
                                          size_t num_vtx, MsqError& err );

    virtual void vertices_get_slaved_flag( const VertexHandle vert_array[], std::vector< bool >& slaved_flag_array,
                                           size_t num_vtx, MsqError& err );

    /**\brief Get vertex coordinates */
    virtual void vertices_get_coordinates( const VertexHandle vert_array[], MsqVertex* coordinates, size_t num_vtx,
                                           MsqError& err );
    /**\brief Set vertex coordinates */
    virtual void vertex_set_coordinates( VertexHandle vertex, const Vector3D& coordinates, MsqError& err );

    /**\brief Set vertex mark */
    virtual void vertex_set_byte( VertexHandle vertex, unsigned char byte, MsqError& err );
    /**\brief Set vertex mark */
    virtual void vertices_set_byte( const VertexHandle* vert_array, const unsigned char* byte_array, size_t array_size,
                                    MsqError& err );

    /**\brief Get vertex mark */
    virtual void vertex_get_byte( VertexHandle vertex, unsigned char* byte, MsqError& err );
    /**\brief Get vertex mark */
    virtual void vertices_get_byte( const VertexHandle* vert_array, unsigned char* byte_array, size_t array_size,
                                    MsqError& err );

    /**\brief Get vertex-to-element adjacencies */
    virtual void vertices_get_attached_elements( const VertexHandle* vertex_array, size_t num_vertices,
                                                 std::vector< ElementHandle >& elements, std::vector< size_t >& offsets,
                                                 MsqError& err );

    /**\brief Get element connectivity */
    virtual void elements_get_attached_vertices( const ElementHandle* elem_handles, size_t num_elems,
                                                 std::vector< VertexHandle >& vertices, std::vector< size_t >& offsets,
                                                 MsqError& err );

    /**\brief Return topology type enum for an array of elements */
    virtual void elements_get_topologies( const ElementHandle* element_handle_array, EntityTopology* element_topologies,
                                          size_t num_elements, MsqError& err );

    //**************** Memory Management ****************
    /**\brief no-op */
    virtual void release_entity_handles( const EntityHandle* handle_array, size_t num_handles, MsqError& err );

    // Instead of deleting a Mesh when you think you are done,
    // call release().  In simple cases, the implementation could
    // just call the destructor.  More sophisticated implementations
    // may want to keep the Mesh object to live longer than Mesquite
    // is using it.
    virtual void release();

    //*************** Tags  ***********

    /** \brief Create a tag
     *
     * Create a user-defined data type that can be attached
     * to any element or vertex in the mesh.  For an opaque or
     * undefined type, use type=BYTE and length=sizeof(..).
     *
     * \param tag_name  A unique name for the data object
     * \param type      The type of the data
     * \param length    Number of values per entity (1->scalar, >1 ->vector)
     * \param default_value Default value to assign to all entities - may be NULL
     * \return - Handle for tag definition
     */
    virtual TagHandle tag_create( const std::string& tag_name, TagType type, unsigned length, const void* default_value,
                                  MsqError& err );

    /** \brief Remove a tag and all corresponding data
     *
     * Delete a tag.
     */
    virtual void tag_delete( TagHandle handle, MsqError& err );

    /** \brief Get handle for existing tag, by name. */
    virtual TagHandle tag_get( const std::string& name, MsqError& err );

    /** \brief Get properites of tag
     *
     * Get data type and number of values per entity for tag.
     * \param handle     moab::Tag to get properties of.
     * \param name_out   Passed back tag name.
     * \param type_out   Passed back tag type.
     * \param length_out Passed back number of values per entity.
     */
    virtual void tag_properties( TagHandle handle, std::string& name_out, TagType& type_out, unsigned& length_out,
                                 MsqError& err );

    /** \brief Set tag values on elements
     *
     * Set the value of a tag for a list of mesh elements.
     * \param handle     The tag
     * \param num_elems  Length of elem_array
     * \param elem_array Array of elements for which to set the tag value.
     * \param tag_data   moab::Tag data for each element, contiguous in memory.
     *                   This data is expected to be

     *                   num_elems*tag_length*sizeof(tag_type) bytes.
     */
    virtual void tag_set_element_data( TagHandle handle, size_t num_elems, const ElementHandle* elem_array,
                                       const void* tag_data, MsqError& err );

    /** \brief Set tag values on vertices
     *
     * Set the value of a tag for a list of mesh vertices.
     * \param handle     The tag
     * \param num_elems  Length of node_array
     * \param node_array Array of vertices for which to set the tag value.
     * \param tag_data   moab::Tag data for each element, contiguous in memory.
     *                   This data is expected to be
     *                   num_elems*tag_length*sizeof(tag_type) bytes.
     */
    virtual void tag_set_vertex_data( TagHandle handle, size_t num_elems, const VertexHandle* node_array,
                                      const void* tag_data, MsqError& err );

    /** \brief Get tag values on elements
     *
     * Get the value of a tag for a list of mesh elements.
     * \param handle     The tag
     * \param num_elems  Length of elem_array
     * \param elem_array Array of elements for which to get the tag value.
     * \param tag_data   Return buffer in which to copy tag data, contiguous
     *                   in memory.  This data is expected to be
     *                   num_elems*tag_length*sizeof(tag_type) bytes.
     */
    virtual void tag_get_element_data( TagHandle handle, size_t num_elems, const ElementHandle* elem_array,
                                       void* tag_data, MsqError& err );

    /** \brief Get tag values on vertices.
     *
     * Get the value of a tag for a list of mesh vertices.
     * \param handle     The tag
     * \param num_elems  Length of elem_array
     * \param elem_array Array of vertices for which to get the tag value.
     * \param tag_data   Return buffer in which to copy tag data, contiguous
     *                   in memory.  This data is expected to be
     *                   num_elems*tag_length*sizeof(tag_type) bytes.
     */
    virtual void tag_get_vertex_data( TagHandle handle, size_t num_elems, const VertexHandle* node_array,
                                      void* tag_data, MsqError& err );

  protected:
    moab::DataType check_valid_flag_tag( moab::Tag tag, const char* which_flag, MsqError& err );

    void set_int_tag( void* tag, void* meshset, int value, MsqError& err );

    /** \brief  Call TSTTM::Arr::getEntArrAdj
     *
     * Common code for \ref vertices_get_attached_elements and
     * \ref elements_get_attached_vertices
     *
     *\param source      Array of handles of source entities to query from
     *\param num_source  The length of \ref source
     *\param target_type The type of entity to query for
     *\param target      The output list of adjacent entities
     *\param offsets     For each entity in \ref source, the offset in
     *                   \ref target at which the corresponding adjacent
     *                   entities are stored. (output)
     */
    void get_adjacent_entities( const moab::EntityHandle* source, size_t num_source, moab::EntityType target_type,
                                std::vector< EntityHandle >& target, std::vector< size_t >& offsets, MsqError& err );

    /** The IMesh instance */
    moab::Core* meshInstance;

  private:
    void init_active_mesh( moab::Core* mesh, MsqError& err, const moab::Tag* fixed_tag, const moab::Tag* slaved_tag );

    void get_flag_data( moab::Tag tag, bool have_tag, moab::DataType type, const VertexHandle vert_array[],
                        std::vector< bool >& flag_array, size_t num_vtx, MsqError& err );

    /** \brief Set tag values */
    void tag_set_data( TagHandle handle, size_t num_elems, const EntityHandle* handle_array, const void* tag_data,
                       MsqError& err );

    /** \brief Get tag values */
    void tag_get_data( TagHandle handle, size_t num_elems, const EntityHandle* handle_array, void* tag_data,
                       MsqError& err );

    /** Have mesh */
    bool haveMesh;
    /** ITAPS entity set handle for elements to improve */
    // moab::EntityHandle elementSet;
    /** ITAPS entity set handle for nodes to move */
    // moab::EntityHandle nodeSet;
    /** std::set containing elements in elementSet, used
     *  to constrain vertex->element adjaceny queries to
     *  only those elements that are in the input element set.
     */
    // std::vector<moab::EntityHandle> inputElements;

    /** The type of elements contained in the input element set.
     * Should be one of:
     * - iBase_REGION    - volume elements
     * - iBase_FACE      - face/2d elements
     * - iBase_ALL_TYPES - mixed volume and face elements
     */
    moab::EntityType inputSetType;
    /** The meshset containing the elements to optimize */
    moab::EntityHandle inputSet;

    /** Handle for tag used to hold vertex byte */
    moab::Tag byteTag;
    /** moab::Tag was created in constructor */
    bool createdByteTag;
    /** Handle for tag used to hold vertex-fixed flag */
    moab::Tag fixedTag;
    /** fixedTag handle is valid */
    bool haveFixedTag;
    /** iBase_TYPE for fixed tag */
    moab::DataType fixedTagType;
    /** Handle for tag used to hold vertex-slaved flag */
    moab::Tag slavedTag;
    /** slavedTag handle is valid */
    bool haveSlavedTag;
    /** iBase_TYPE for slaved tag */
    moab::DataType slavedTagType;
    /** Dimension is queried once during create and cached */
    int geometricDimension;
    /** Map iMesh_EntityTopology to MBMesquite::EntityTopology */
    EntityTopology topologyMap[moab::MBMAXTYPE + 1];
};

}  // namespace MBMesquite

#endif  // MSQ_MOAB_HPP
